#include <fstream>
#include <iostream>
#include "CCardMgr.h"
#include "pb_cfg_card.pb.h"
#include "MsgCard.pb.h"
#include "MsgComm.pb.h"
#include "commclass.h"
#include "MsgStorage.pb.h"
#include "PBMsgHead.pb.h"
#include "commmsg.h"
#include "CSMsgDef.h"
#include "../mainctrl.h"

int CCardMgr::LoadConfig(const char* szCfgPath)
{
    int iRet = 0;

    if (szCfgPath == NULL)
    {
        CT_ERROR(("Card Cfg Path is null"));
        return RET_SYSTEM_FAIL;
    }

    pb_cfg_card stCardPBConfig;
    std::fstream stFileInput(szCfgPath, std::ios::in | std::ios::binary);
    if (!stCardPBConfig.ParseFromIstream(&stFileInput))
    {
        CT_ERROR(("Failed to Parse CardCfg! %s", szCfgPath));
        return RET_SYSTEM_FAIL;
    }

    //解析配置文件内容
    //卡片配置
    for (int i = 0; i < stCardPBConfig.card_list_rows_size(); ++i)
    {
        const pb_cfg_card_pb_cfg_card_list_unit& stCardPBConfigUnit
            = stCardPBConfig.card_list_rows(i);

        CCardConfigData stConfigData;
        stConfigData._usCardConfigID = (unsigned short)stCardPBConfigUnit.id();
        stConfigData._ucCardType = (unsigned char)stCardPBConfigUnit.type();
        stConfigData._usCardPoint = (unsigned short)stCardPBConfigUnit.point();
        stConfigData._bDefaultCanWare = (stCardPBConfigUnit.default_can_ware() != 0);
        
        stConfigData._ucAttrType1 = (unsigned char)stCardPBConfigUnit.attr_type1();
        stConfigData._usAttrValue1 = (unsigned short)stCardPBConfigUnit.attr_value1();
        stConfigData._ucAttrType2 = (unsigned char)stCardPBConfigUnit.attr_type2();
        stConfigData._usAttrValue2 = (unsigned short)stCardPBConfigUnit.attr_value2();

        stConfigData._astDropRate.AddOneItem(stCardPBConfigUnit.drop_rate1());
        stConfigData._astDropRate.AddOneItem(stCardPBConfigUnit.drop_rate2());
        stConfigData._astDropRate.AddOneItem(stCardPBConfigUnit.drop_rate3());
        stConfigData._astDropRate.AddOneItem(stCardPBConfigUnit.drop_rate4());
        stConfigData._astDropRate.AddOneItem(stCardPBConfigUnit.drop_rate5());
        stConfigData._astDropRate.AddOneItem(stCardPBConfigUnit.drop_rate6());
        stConfigData._astDropRate.AddOneItem(stCardPBConfigUnit.drop_rate7());
        stConfigData._astDropRate.AddOneItem(stCardPBConfigUnit.drop_rate8());
        stConfigData._astDropRate.AddOneItem(stCardPBConfigUnit.drop_rate9());
        stConfigData._astDropRate.AddOneItem(stCardPBConfigUnit.drop_rate10());

        //兑换相关
        stConfigData._bCanExchange = stCardPBConfigUnit.can_exchange() != 0;
        stConfigData._usExchangeLevel = stCardPBConfigUnit.exchange_need_level();
        stConfigData._iExchangePrice = stCardPBConfigUnit.exchange_price();

        iRet = _stCardConfig.AddConfig(stConfigData);
        CHECK_RETURN(iRet);
    }

    //占卜配置
    for (int i = 0; i < stCardPBConfig.divine_config_rows_size(); ++i)
    {
        const pb_cfg_card_pb_cfg_divine_config_unit& stCardPBConfigUnit
            = stCardPBConfig.divine_config_rows(i);

        CDivinePosConfigData stConfigData;

        stConfigData._ucDivinePos = (unsigned char)stCardPBConfigUnit.divine_pos();
        stConfigData._uiCostSilver = (unsigned int)stCardPBConfigUnit.silver_cost();
        stConfigData._ucSuccCardType = (unsigned char)stCardPBConfigUnit.succ_card_type();
        
        unsigned char ucCardType = (unsigned char)stCardPBConfigUnit.card_type1();
        unsigned short usDropCount = (unsigned short)stCardPBConfigUnit.drop_count1();
        unsigned short usDropRate = (unsigned short)stCardPBConfigUnit.drop_rate1();
        iRet = stConfigData.AddDrop(stConfigData._ucDivinePos, ucCardType, usDropRate, usDropCount);
        CHECK_RETURN(iRet);

        if (stCardPBConfigUnit.card_type2() != 0)
        {
            ucCardType = (unsigned char)stCardPBConfigUnit.card_type2();
            usDropCount = (unsigned short)stCardPBConfigUnit.drop_count2();
            usDropRate = (unsigned short)stCardPBConfigUnit.drop_rate2();
            iRet = stConfigData.AddDrop(stConfigData._ucDivinePos, ucCardType, usDropRate, usDropCount);
            CHECK_RETURN(iRet);
        }

        if (stCardPBConfigUnit.card_type3() != 0)
        {
            ucCardType = (unsigned char)stCardPBConfigUnit.card_type3();
            usDropCount = (unsigned short)stCardPBConfigUnit.drop_count3();
            usDropRate = (unsigned short)stCardPBConfigUnit.drop_rate3();
            iRet = stConfigData.AddDrop(stConfigData._ucDivinePos, ucCardType, usDropRate, usDropCount);
            CHECK_RETURN(iRet);
        }

        iRet = _stDivinePosConfig.AddConfig(stConfigData);
        CHECK_RETURN(iRet);
    }

    //卡片经验
    for (int i = 0; i < stCardPBConfig.card_level_rows_size(); ++i)
    {
        const pb_cfg_card_pb_cfg_card_level_unit& stCardPBConfigUnit
            = stCardPBConfig.card_level_rows(i);

        CCardLevelConfigData stConfigData;

        stConfigData._usLevel = (unsigned short)stCardPBConfigUnit.level();
        stConfigData._uiType2Exp = (unsigned int)stCardPBConfigUnit.type_2_exp();
        stConfigData._uiType3Exp = (unsigned int)stCardPBConfigUnit.type_3_exp();
        stConfigData._uiType4Exp = (unsigned int)stCardPBConfigUnit.type_4_exp();
        stConfigData._uiType5Exp = (unsigned int)stCardPBConfigUnit.type_5_exp();
        stConfigData._uiType6Exp = (unsigned int)stCardPBConfigUnit.type_6_exp();
        stConfigData._uiType8Exp = (unsigned int)stCardPBConfigUnit.type_8_exp();

        iRet = _stCardLevelConfig.AddConfig(stConfigData);
        CHECK_RETURN(iRet);
    }

    //命运漩涡配置
    for (int i = 0; i < stCardPBConfig.vortex_level_rows_size(); ++i)
    {
        const pb_cfg_card_pb_cfg_vortex_level_unit& stCardPBConfigUnit
            = stCardPBConfig.vortex_level_rows(i);

        CVortexConfigData stConfigData;

        stConfigData._usLevel = (unsigned short)stCardPBConfigUnit.level();
        stConfigData._uiNeedFateForceCount = (unsigned int)stCardPBConfigUnit.need_count();
        stConfigData._usAddProp = (unsigned short)stCardPBConfigUnit.add_prop();
        stConfigData._uiExtractNeedSilver = (unsigned int)stCardPBConfigUnit.extract_need_silver();

        iRet = _stVortexConfig.AddConfig(stConfigData);
        CHECK_RETURN(iRet);
    }

    //金币占卜
    for (int i = 0; i < stCardPBConfig.gold_divine_rows_size(); ++i)
    {
        const pb_cfg_card_pb_cfg_gold_divine_unit& stCardPBConfigUnit
            = stCardPBConfig.gold_divine_rows(i);

        iRet = _stDivinePosConfig.AddGoldDrop(stCardPBConfigUnit.card_type(), stCardPBConfigUnit.rate());
        CHECK_RETURN(iRet);
    }

    return 0;
}

//翻牌
int CCardMgr::TurnOverCard(CPlayerBaseInfo& stPlayerBaseInfo, CPlayerDetailInfo& stPlayerDetailInfo, MsgAnsTurnOver& stMsgAnsTurnOver)
{
    int iRet = 0;
    
    CUserDivineData& stDivineData = stPlayerDetailInfo._stUserDivine;

    //数据检查
    if (stDivineData.ucCardPos >= MAX_CARD_DIVINE_POS_CONIFG)
    {
        stDivineData.ucCardPos = 0;
    }

    if (stDivineData.IsFull())
    {
        return ERROR_CARD_DIVINE_FULL;
    }

    //获取配置
    unsigned char ucDestPos = stDivineData.ucCardPos + 1;
    stMsgAnsTurnOver.set_card_pos(ucDestPos);

    CDivinePosConfigData stConfigData;
    iRet = _stDivinePosConfig.GetConfig(ucDestPos, stConfigData);
    CHECK_RETURN(iRet);

    //扣除银币
    iRet = CMainCtrl::m_pstMain->m_stItemMgr.SubSilver(stPlayerBaseInfo, (int)stConfigData._uiCostSilver, 0);
    CHECK_RETURN(iRet);

    //翻牌
    CRateTarget stDrop;
    unsigned short usDropCount = 0;

    if (stDivineData._ucTurnOverCardCount < 2)
    {
        while (1)
        {
            usDropCount = 0;
            iRet = stConfigData.Drop(stDrop, ucDestPos, usDropCount);
            CHECK_RETURN(iRet);

            if (stDrop._iTargetID != CARD_TYPE_SILVER)
            {
                stDivineData._ucTurnOverCardCount++;
                break;
            }
        }
    }
    else
    {        
        iRet = stConfigData.Drop(stDrop, ucDestPos, usDropCount);
        CHECK_RETURN(iRet);
    }
    
    unsigned char ucCardType = (unsigned char)stDrop._iTargetID;
    stMsgAnsTurnOver.set_card_type(ucCardType);
    stMsgAnsTurnOver.set_count(usDropCount);
    switch(ucCardType)
    {
    case CARD_TYPE_SILVER:
        stDivineData._iSilver += usDropCount;
        break;
    case CARD_TYPE_FATE_FORCE:
        stDivineData._shFateForce += (char)usDropCount;
        break;
    case CARD_TYPE_GREEN_CARD:
        stDivineData._cGreenCard += (char)usDropCount;
        break;
    case CARD_TYPE_BLUE_CARD:
        stDivineData._cBlueCard += (char)usDropCount;
        break;
    case CARD_TYPE_PURPLE_CARD:
        stDivineData._cPurpleCard += (char)usDropCount;
        break;
    case CARD_TYPE_SOUL_CARD:
        stDivineData._cSoulCard += (char)usDropCount;
        break;
    case CARD_TYPE_GOLD_CARD:
        stDivineData._cGoldCard += (char)usDropCount;
        break;
    case CARD_TYPE_ROMANY:
        stDivineData._cRomany += (char)usDropCount;
        break;
    default:
        return ERROR_CARD_TYPE;
    }

    if (CARD_TYPE_GOLD_CARD == ucCardType)
    {
        //精彩活动：金秋猎金
        CMainCtrl::m_pstMain->m_stActMgr.TriggerGetCard(stPlayerDetailInfo._stSplendidAct, 
            stPlayerBaseInfo._uiGID, ucCardType, 0, (int)usDropCount);
    }
    
    //printf("====card %d====\n", ucGetCardType);
    //检查状态
    if (ucCardType != stConfigData._ucSuccCardType)
    {
        //printf("====card restore====\n");
        stDivineData.ucCardPos = 0;
    }
    else
    {
        stDivineData.ucCardPos = ucDestPos;
    }

    return 0;
}

//金币占卜
int CCardMgr::GoldTurnOverCard(CPlayerBaseInfo& stPlayerBaseInfo, CPlayerDetailInfo& stPlayerDetailInfo, int iMaxCanDivineCount, MsgAnsTurnOver& stMsgAnsTurnOver)
{
    int iRet = 0;

    //获取背包
    int iIndex = CMainCtrl::m_pstMain->m_stPlayerOnlineDataHash.HashFind(stPlayerBaseInfo._uiGID);
    if (iIndex < 0)
    {
        CT_ERROR((""));
        return RET_SYS_ERROR;
    }
    CUserStorage& stUserStorage = CMainCtrl::m_pstMain->m_stPlayerOnlineDataHash[iIndex]._stUserStorage;
    CUserDivineData& stDivineData = stPlayerDetailInfo._stUserDivine;

    //检查次数
    if (stDivineData._stHasGoldDivineCount.Get(CMainCtrl::m_pstMain->m_iNow) >= iMaxCanDivineCount)
    {
        return ERROR_CARD_MAX_GOLD_DIVINE;
    }

    bool bUseGold = false;

    //检查魔源水晶
    if (CMainCtrl::m_pstMain->m_stStorageMgr.GetItemCount(stUserStorage, ITEM_TYPE_MATERIAL, CARD_GOLD_DIVINE_USE_MATERIAL) <= 0)
    {        
        if (stPlayerBaseInfo._iGold < MAX_CARD_GOLD_DIVINE_NEED_GOLD)
        {
            return ERROR_CARD_GOLD_DIVINE_NEED_MATERIAL;
        }

        bUseGold = true;
    }

    //检查是否已满
    if (stDivineData._ausGoldDivineCards.Size() == stDivineData._ausGoldDivineCards.GetCount())
    {
        return ERROR_CARD_GOLD_DIVINE_FULL;
    }

    //roll卡片
    CRateTarget stDrop;
    iRet = _stDivinePosConfig.GoldDrop(stDrop);
    CHECK_RETURN(iRet);

    unsigned char ucCardType = (unsigned char)stDrop._iTargetID;
    unsigned short usCardConfigID = 0;

    if (CARD_TYPE_ROMANY != ucCardType)
    {
        iRet = _stCardConfig.GoldDrop(stPlayerBaseInfo._shLevel, ucCardType, usCardConfigID);
        CHECK_RETURN(iRet);

        if (usCardConfigID == 0)
        {
            CT_ERROR((""));
            return RET_SYS_ERROR;
        }

        CT_TRACE(("gold drop card config id %d", usCardConfigID));
    }
    
    //扣除魔源水晶
    unsigned int uiNotifyType = 0;
    if (false == bUseGold)
    {
        iRet = CMainCtrl::m_pstMain->m_stStorageMgr.TakeOutItem(stUserStorage, ITEM_TYPE_MATERIAL, CARD_GOLD_DIVINE_USE_MATERIAL, 1);         
        CHECK_RETURN(iRet);

        CMainCtrl::m_pstMain->m_stStorageMgr.GetData(stUserStorage, stPlayerDetailInfo._stUserStorage);

        uiNotifyType = NOTIFY_BAG_INFO | NOTIFY_USER_MONEY;
    }
    else
    {
        iRet = CMainCtrl::m_pstMain->m_stItemMgr.SubGold(stPlayerBaseInfo, OSS_MONEY_MODULE_39,  MAX_CARD_GOLD_DIVINE_NEED_GOLD);
        CHECK_RETURN(iRet);

        uiNotifyType = NOTIFY_USER_MONEY;
    }

    //添加次数
    stDivineData._stHasGoldDivineCount.Add(1, CMainCtrl::m_pstMain->m_iNow);

    //发放
    if (CARD_TYPE_ROMANY != ucCardType)
    {
        //添加卡片
        stDivineData._ausGoldDivineCards.AddOneItem(usCardConfigID);

        if (CARD_TYPE_GOLD_CARD == ucCardType)
        {
            //精彩活动：金秋猎金
            CMainCtrl::m_pstMain->m_stActMgr.TriggerGetCard(stPlayerDetailInfo._stSplendidAct, 
                stPlayerBaseInfo._uiGID, ucCardType, 0, 1);
        }
    }
    else
    {
        stPlayerBaseInfo._iRomany++;
    }

    //通知前台数据
    stMsgAnsTurnOver.set_romany(stPlayerBaseInfo._iRomany);
    for (int i = 0; i < stDivineData._ausGoldDivineCards.Size(); ++i)
    {
        stMsgAnsTurnOver.add_gold_divine_bag(stDivineData._ausGoldDivineCards[i]);
    }

    CMainCtrl::m_pstMain->m_stNotifyMgr.Notify(stPlayerBaseInfo._uiGID, uiNotifyType);
    
    return 0;
}

int CCardMgr::GoldDropCard(CUserCard& stUserCard, CPlayerBaseInfo& stPlayerBaseInfo, CPlayerDetailInfo& stPlayerDetailInfo, MsgAnsTurnOver& stMsgAnsTurnOver)
{
    int iRet = 0;
    CUserDivineData& stDivineData = stPlayerDetailInfo._stUserDivine;

    if (stDivineData._ausGoldDivineCards.Size() == 0)
    {
        return ERROR_CARD_GOLD_DIVINE_EMPTY;
    }

    if (stUserCard._stCardStorage.GetFreeGridCount(true) == 0)
    {
        return ERROR_CARD_BAG_FULL;
    }

    while (stDivineData._ausGoldDivineCards.Size() > 0
        && stUserCard._stCardStorage.GetFreeGridCount(true) > 0)
    {
        unsigned short usCardConfigID = stDivineData._ausGoldDivineCards[0];
        iRet = DropCard(stUserCard, usCardConfigID, 0);
        CHECK_RETURN(iRet);

        stDivineData._ausGoldDivineCards.DelOneItem(0);
    }

    //通知前台数据
    for (int i = 0; i < stDivineData._ausGoldDivineCards.Size(); ++i)
    {
        stMsgAnsTurnOver.add_gold_divine_bag(stDivineData._ausGoldDivineCards[i]);
    }

    return 0;
}

//初始化数据
int CCardMgr::InitData(CUserCard& stUserCard, unsigned int uiGID, CUserCardData& stUserCardData)
{
    int iRet = 0;
    stUserCard.Clear();
    stUserCard._uiGID = uiGID;
    stUserCard._iNowCardID = stUserCardData._iNowCardID;

    for (int i = 0; i < stUserCardData._astCardDatas.Size(); ++i)
    {
        iRet = AddCard(stUserCard, stUserCardData._astCardDatas[i]);
        CHECK_RETURN(iRet);
    }
    
    return 0;
}

int CCardMgr::AddCard(CUserCard& stUserCard, CSingleCardData& stSingleCardData)
{
    int iRet = 0;

    //添加hash
    int iIndex = stUserCard._stCardHash.HashFind(stSingleCardData._uiCardID);
    if (iIndex >= 0)
    {
        CT_ERROR(("card id = %u", stSingleCardData._uiCardID));
        return RET_SYS_ERROR;
    }

    iIndex = stUserCard._stCardHash.HashAlloc(stSingleCardData._uiCardID);
    if (iIndex < 0)
    {
        CT_ERROR(("card full gid %u", stUserCard._uiGID));
        return RET_SYS_ERROR;
    }

    stUserCard._stCardHash[iIndex]._stCardData = stSingleCardData;

    if (stSingleCardData._ucIsWare != 0)
    {
        return 0; //已经穿上，不需要加入背包
    }

    //加入背包
    iRet = AddToBag(stUserCard, stSingleCardData);
    CHECK_RETURN(iRet);

    //设置位置
    stUserCard._stCardHash[iIndex]._stCardData._usPos = stSingleCardData._usPos;

    //OSS账单
    CMainCtrl::m_pstMain->m_stOssMgr.AddProduceItem(ITEM_TYPE_CARD, stSingleCardData._usCardConfigID, 1, stSingleCardData._uiCardID);

    return 0;
}

int CCardMgr::GetData(CUserCard& stUserCard, CUserCardData& stUserCardData)
{
    stUserCardData.Clear();
    stUserCardData._iNowCardID = stUserCard._iNowCardID;

    for (int iIndex = stUserCard._stCardHash.GetHeadIndex();
        iIndex >= 0;
        iIndex = stUserCard._stCardHash.GetNextIndex(iIndex))
    {
        stUserCardData._astCardDatas.AddOneItem(stUserCard._stCardHash[iIndex]._stCardData);
    }

    return 0;
}

int CCardMgr::GetCardInfo(CUserCard& stUserCard, unsigned int uiCardID, CSingleCardData& stCardData)
{
    int iIndex = stUserCard._stCardHash.HashFind(uiCardID);
    if (iIndex < 0)
    {
        return ERROR_CARD_NOT_EXIST;
    }
    stCardData = stUserCard._stCardHash[iIndex]._stCardData;
    return 0;
}

int CCardMgr::GetCardInfo(CUserCard& stUserCard, CPlayerDetailInfo& stPlayerDetailInfo, unsigned int uiCardID, MsgGridItemInfo& stCardBaseInfo)
{
    int iRet = 0;
    int iIndex = stUserCard._stCardHash.HashFind(uiCardID);
    if (iIndex < 0)
    {
        return ERROR_CARD_NOT_EXIST;
    }
    CSingleCardData& stCardData = stUserCard._stCardHash[iIndex]._stCardData;

    CCardConfigData stCardConfigData;
    iRet = _stCardConfig.GetConfig(stCardData._usCardConfigID, stCardConfigData);
    CHECK_RETURN(iRet);

    stCardBaseInfo.set_item_store_pos(stCardData._usPos);
    stCardBaseInfo.set_item_type(ITEM_TYPE_CARD);
    stCardBaseInfo.set_item_config_id(stCardData._usCardConfigID);
    stCardBaseInfo.set_item_count(1);
    stCardBaseInfo.set_item_aux_info(CTStringUtils::Uint64ToString(uiCardID, 10));

    return 0;
}

int CCardMgr::GetCardInfo(CUserCard& stUserCard, CPlayerDetailInfo& stPlayerDetailInfo, unsigned int uiCardID, MsgAnsItemBaseInfos& stCardItemBaseInfo)
{
    stCardItemBaseInfo.set_gid(stPlayerDetailInfo._uiGID);

    int iIndex = stUserCard._stCardHash.HashFind(uiCardID);
    if (iIndex < 0)
    {
        return ERROR_CARD_NOT_EXIST;
    }
    CSingleCardData& stCardData = stUserCard._stCardHash[iIndex]._stCardData;
    
    if (stCardData._ucIsWare)
    {
        stCardItemBaseInfo.set_partner_id(stCardData._iWareRoleID);
        stCardItemBaseInfo.set_storage_type(STORAGE_TYPE_ROLE_CARD);
    }
    else    
    {
        stCardItemBaseInfo.set_storage_type(STORAGE_TYPE_CARD);
    }
    
    MsgGridItemInfo* pstMsgGridItemInfo = stCardItemBaseInfo.add_infos();
    return GetCardInfo(stUserCard, stPlayerDetailInfo, uiCardID, *pstMsgGridItemInfo);
}

int CCardMgr::GetCardInfo(CUserCard& stUserCard, CPlayerDetailInfo& stPlayerDetailInfo, unsigned int uiCardID, MsgCardDetailInfo& stCardDetailInfo)
{
    DumpCardInfo(stUserCard);
    int iRet = 0;
    int iIndex = stUserCard._stCardHash.HashFind(uiCardID);
    if (iIndex < 0)
    {
        return ERROR_CARD_NOT_EXIST;
    }
    CSingleCardData& stCardData = stUserCard._stCardHash[iIndex]._stCardData;

    CCardConfigData stCardConfigData;
    iRet = _stCardConfig.GetConfig(stCardData._usCardConfigID, stCardConfigData);
    CHECK_RETURN(iRet);

    stCardDetailInfo.set_card_id(uiCardID);
    stCardDetailInfo.set_card_conf_id(stCardConfigData._usCardConfigID);
    stCardDetailInfo.set_level(stCardData._usLevel);
    stCardDetailInfo.set_now_exp(stCardData._iNowExp);

    unsigned int uiLevelUpExp = 0;
    if (stCardData._usLevel < _stCardLevelConfig._stMaxLevelConfigData._usLevel)
    {
        _stCardLevelConfig.GetConfig(stCardData._usLevel + 1, stCardConfigData._ucCardType, uiLevelUpExp);
    }
    else
    {
        stCardDetailInfo.set_level_up_exp(_stCardLevelConfig._stMaxLevelConfigData.GetExp(stCardConfigData._ucCardType, uiLevelUpExp));
    }
    stCardDetailInfo.set_level_up_exp(uiLevelUpExp);

    stCardDetailInfo.set_can_ware(stCardData._ucCanWare);
    stCardDetailInfo.set_point(stCardConfigData._usCardPoint);

    //获取命运漩涡等级
    unsigned short usVortexLevel = 0;
    if (stCardData._ucIsWare)
    {
        int iRoleID = stCardData._iWareRoleID;
        if (iRoleID == 0) //主角
        {
            usVortexLevel = stPlayerDetailInfo._stCardVortex._usLevel;
        }
        else
        {
            iIndex = stPlayerDetailInfo._stUserPartner.FindActData(iRoleID);
            if (iIndex < 0)
            {
                return ERROR_CARD_NO_PARTNER;
            }

            CPartnerActData& stPartner = stPlayerDetailInfo._stUserPartner.GetPartnerActData(iIndex);
            usVortexLevel = stPartner._stCardVortex._usLevel;
        }
    }

    CVortexConfigData stVortexConfigData;
    iRet = _stVortexConfig.GetConfig(usVortexLevel, stVortexConfigData);
    CHECK_RETURN(iRet);
    unsigned short usAddDrop = stVortexConfigData._usAddProp;
    
    MsgAttrDetail* pstMsgAttrDetail = stCardDetailInfo.add_attr_detail();
    pstMsgAttrDetail->set_attr_type(stCardConfigData._ucAttrType1);
    pstMsgAttrDetail->set_attr_value((int)(1.0 * stCardConfigData._usAttrValue1 * stCardData._usLevel * (usAddDrop + COMM_MOD) / COMM_MOD));

    if (stCardConfigData._ucAttrType2 != 0)
    {
        MsgAttrDetail* pstMsgAttrDetail = stCardDetailInfo.add_attr_detail();
        pstMsgAttrDetail->set_attr_type(stCardConfigData._ucAttrType2);
        pstMsgAttrDetail->set_attr_value((int)(1.0 * stCardConfigData._usAttrValue2 * stCardData._usLevel * (usAddDrop + COMM_MOD) / COMM_MOD));
    }

    return 0;
}

int CCardMgr::GetCardAttr(CUserCard& stUserCard, int iRoleID, CPlayerDetailInfo& stPlayerDetailInfo, CPlayerAttr& stPlayerAttr)
{   
    int iRet = 0;

    //获取命运漩涡等级
    unsigned short usVortexLevel = 0;
    int iIndex = 0;

    CPlayerWareCard stPlayerWareCard;
    if (iRoleID == 0) //主角
    {
        usVortexLevel = stPlayerDetailInfo._stCardVortex._usLevel;
        stPlayerWareCard = stPlayerDetailInfo._stPlayerWareCard;
    }
    else
    {
        iIndex = stPlayerDetailInfo._stUserPartner.FindActData(iRoleID);
        if (iIndex < 0)
        {
            return ERROR_CARD_NO_PARTNER;
        }

        CPartnerActData& stPartner = stPlayerDetailInfo._stUserPartner.GetPartnerActData(iIndex);
        stPlayerWareCard = stPartner._stPlayerWareCard;
        usVortexLevel = stPartner._stCardVortex._usLevel;
    }

    CVortexConfigData stVortexConfigData;
    iRet = _stVortexConfig.GetConfig(usVortexLevel, stVortexConfigData);
    CHECK_RETURN(iRet);
    unsigned short usAddDrop = stVortexConfigData._usAddProp;

    //
    for (int i = 0; i < stPlayerWareCard._astPlayerCard.Size(); ++i)
    {
        unsigned int uiCardID = stPlayerWareCard._astPlayerCard[i];
        if (uiCardID == 0)
        {
            continue;
        }

        int iIndex = stUserCard._stCardHash.HashFind(uiCardID);
        if (iIndex < 0)
        {
            return ERROR_CARD_NOT_EXIST;
        }
        CSingleCardData& stCardData = stUserCard._stCardHash[iIndex]._stCardData;

        //获取配置
        CCardConfigData stCardConfigData;
        iRet = _stCardConfig.GetConfig(stCardData._usCardConfigID, stCardConfigData);
        CHECK_RETURN(iRet);

        //计算属性
        stPlayerAttr.AddAttr(stCardConfigData._ucAttrType1, (int)(1.0 * stCardConfigData._usAttrValue1 * stCardData._usLevel * (usAddDrop + COMM_MOD) / COMM_MOD));
        stPlayerAttr.AddAttr(stCardConfigData._ucAttrType2, (int)(1.0 * stCardConfigData._usAttrValue2 * stCardData._usLevel * (usAddDrop + COMM_MOD) / COMM_MOD));
    }

    return 0;
}

int CCardMgr::GetTotalLevel(CUserCard& stUserCard, CPlayerWareCard& stPlayerWareCard, int& iTotalLevel)
{
    iTotalLevel = 0;

    for (int i = 0; i < stPlayerWareCard._astPlayerCard.Size(); ++i)
    {
        unsigned int uiCardID = stPlayerWareCard._astPlayerCard[i];
        if (uiCardID == 0)
        {
            continue;
        }

        int iIndex = stUserCard._stCardHash.HashFind(uiCardID);
        if (iIndex < 0)
        {
            return ERROR_CARD_NOT_EXIST;
        }
        CSingleCardData& stCardData = stUserCard._stCardHash[iIndex]._stCardData;

        iTotalLevel += stCardData._usLevel;
    }

    return 0;
}

int CCardMgr::GetCardInfoByPos(CUserCard& stUserCard, CPlayerDetailInfo& stPlayerDetailInfo, unsigned short usBagPos, MsgCardDetailInfo& stCardDetailInfo)
{
    int iRet = 0;
    CStorageGrid stGrid;
    iRet = stUserCard._stCardStorage.GetGridInfoByPos(true, usBagPos, stGrid);
    CHECK_RETURN(iRet);

    if (stGrid.IsEmpty())
    {
        return 0;
    }

    return GetCardInfo(stUserCard, stPlayerDetailInfo, stGrid._stKey._uiItemID, stCardDetailInfo);
}

int CCardMgr::GetCardInfoByPos(CUserCard& stUserCard, CPlayerDetailInfo& stPlayerDetailInfo, unsigned short usBagPos, MsgGridItemInfo& stBaseCardInfo)
{
    int iRet = 0;
    CStorageGrid stGrid;
    iRet = stUserCard._stCardStorage.GetGridInfoByPos(true, usBagPos, stGrid);
    CHECK_RETURN(iRet);

    if (stGrid.IsEmpty())
    {
        return 0;
    }

    return GetCardInfo(stUserCard, stPlayerDetailInfo, stGrid._stKey._uiItemID, stBaseCardInfo);
}

int CCardMgr::DropCard(CUserCard& stUserCard, CPlayerBaseInfo& stPlayerBaseInfo, 
             CPlayerDetailInfo& stPlayerDetailInfo, bool bIsMerge)
{
    int iRet = 0;
    CUserDivineData& stUserDivineData = stPlayerDetailInfo._stUserDivine;
    unsigned short usRoleLevel = (unsigned short)stPlayerBaseInfo._shLevel;

    //吉普赛银币
    stPlayerBaseInfo._iRomany += stUserDivineData._cRomany;
    stUserDivineData._cRomany = 0;

    //金色卡片
    for (; stUserDivineData._cGoldCard > 0; --stUserDivineData._cGoldCard)
    {
        iRet = DropCard(stUserCard, CARD_TYPE_GOLD_CARD, usRoleLevel, 0);
        CHECK_RETURN(iRet);
    }

    //卡魂
    for (; stUserDivineData._cSoulCard > 0; --stUserDivineData._cSoulCard)
    {
        iRet = DropCard(stUserCard, CARD_TYPE_SOUL_CARD, usRoleLevel, 0);
        CHECK_RETURN(iRet);
    }

    //紫色卡片
    for (; stUserDivineData._cPurpleCard > 0; --stUserDivineData._cPurpleCard)
    {
        iRet = DropCard(stUserCard, CARD_TYPE_PURPLE_CARD, usRoleLevel, 0);
        CHECK_RETURN(iRet);
    }

    if (bIsMerge)
    {
        if (stUserCard._stCardStorage.BagIsFull())
        {
            return ERROR_CARD_BAG_FULL;
        }

        unsigned char ucDestCardType = 0;
        int iNowExp = 0;

        if (stUserDivineData._cBlueCard > 0)    //蓝色卡片
        {
            ucDestCardType = CARD_TYPE_BLUE_CARD;
            stUserDivineData._cBlueCard--;          //抽取一张用于掉落
        }
        else if (stUserDivineData._cGreenCard > 0)  //绿色卡片
        {
            ucDestCardType = CARD_TYPE_GREEN_CARD;
            stUserDivineData._cGreenCard--;
        }

        if (ucDestCardType != 0)
        {
            iRet = ExtractCardTotalExp(stUserDivineData, iNowExp);
            CHECK_RETURN(iRet);

            iRet = DropCard(stUserCard, ucDestCardType, usRoleLevel, iNowExp);
            CHECK_RETURN(iRet);
        }
    }
    else
    {
        //蓝色卡片
        for (; stUserDivineData._cBlueCard > 0; --stUserDivineData._cBlueCard)
        {
            iRet = DropCard(stUserCard, CARD_TYPE_BLUE_CARD, usRoleLevel, 0);
            CHECK_RETURN(iRet);
        }

        //绿色卡片
        for (; stUserDivineData._cGreenCard > 0; --stUserDivineData._cGreenCard)
        {
            iRet = DropCard(stUserCard, CARD_TYPE_GREEN_CARD, usRoleLevel, 0);
            CHECK_RETURN(iRet);
        }
    }

    //命运之力
    stPlayerBaseInfo._iFateForce += stUserDivineData._shFateForce;
    stUserDivineData._shFateForce = 0;

    stPlayerBaseInfo.AddSilver(stPlayerBaseInfo._i64Silver, stUserDivineData._iSilver);

    stUserDivineData._iSilver = 0; 

    CT_TRACE(("CCardMgr: ============== after drop =============="));
    DumpCardInfo(stUserCard);

    return 0;
}

//一键合成卡片背包
int CCardMgr::MergeCardBag(CUserCard& stUserCard, CPlayerDetailInfo& stPlayerDetailInfo)
{
    int iRet = 0;

    CT_TRACE(("CCardMgr: ============== before merge =============="));
    DumpCardInfo(stUserCard);

    CUserStorage& stCardStorage = stUserCard._stCardStorage;
    iRet = stCardStorage.SortStorage(true);
    CHECK_RETURN(iRet);

    if (stCardStorage.GetBagHeadIndex() < 0)
    {
        return 0;       //卡片包是空的
    }

    //确定目标卡片位置
    unsigned short usDestCardPos = 0;

    CStorageMgr::T_STORAGE_GRIDS astStorageGrid;
    CMainCtrl::m_pstMain->m_stStorageMgr.GetBagList(stUserCard._stCardStorage, astStorageGrid);

    for (unsigned short i = 0; i < astStorageGrid.Size(); ++i)
    {
        CStorageGrid& stGrid = astStorageGrid[i];
        if (stGrid._stKey._ucItemType == CARD_TYPE_SOUL_CARD) //卡魂不作为合成目标
        {
            continue;
        }
        else
        {
            //获取目标卡片信息
            unsigned int uiCardID = stGrid._stKey._uiItemID;
            int iIndex = stUserCard._stCardHash.HashFind(uiCardID);
            if (iIndex < 0)
            {
                CT_ERROR(("no this card, id %u", uiCardID));
                return RET_SYS_ERROR;
            }
            CSingleCardData& stDestCardData = stUserCard._stCardHash[iIndex]._stCardData;

            if (stDestCardData._usLevel >= _stCardLevelConfig._stMaxLevelConfigData._usLevel)
            {
                continue;
            }
            else
            {
                usDestCardPos = stGrid._stKey._usStorePos;
                break;
            }
        }
    }

    //获取目标卡片
    CStorageGrid stDestGrid;
    iRet = stCardStorage.GetGridInfoByPos(true, usDestCardPos, stDestGrid);
    CHECK_RETURN(iRet);

    //获取目标卡片信息
    unsigned int uiCardID = stDestGrid._stKey._uiItemID;
    int iIndex = stUserCard._stCardHash.HashFind(uiCardID);
    if (iIndex < 0)
    {
        CT_ERROR(("no this card, id %u", uiCardID));
        return RET_SYS_ERROR;
    }
    CSingleCardData& stDestCardData = stUserCard._stCardHash[iIndex]._stCardData;

    //获取经验上限
    unsigned int uiMaxExp = 0;
    iRet = _stCardLevelConfig._stMaxLevelConfigData.GetExp(stDestGrid._stKey._ucItemType, uiMaxExp);
    CHECK_RETURN(iRet);

    //合成
    for (unsigned short i = 0; i < astStorageGrid.Size(); ++i)
    {
        CStorageGrid& stGrid = astStorageGrid[i];
        if (stGrid._stKey._usStorePos == usDestCardPos)
        {
            continue;
        }

        //获取卡片信息
        CStorageGrid stTakeoutGrid;
        iRet = stCardStorage.GetGridInfoByPos(true, stGrid._stKey._usStorePos, stTakeoutGrid);
        CHECK_RETURN(iRet);

        //获取卡片经验
        int iIndex = stUserCard._stCardHash.HashFind(stTakeoutGrid._stKey._uiItemID);
        if (iIndex < 0)
        {
            CT_ERROR(("no card , id %u", stTakeoutGrid._stKey._uiItemID));
            return RET_SYS_ERROR;
        }
        CSingleCardData& stSourceCardData = stUserCard._stCardHash[iIndex]._stCardData;

        if (stTakeoutGrid._stKey._ucItemType != CARD_TYPE_SOUL_CARD) //卡魂总是被合，不用做下面的校验
        {
            if (stDestGrid._stKey._ucItemType < stTakeoutGrid._stKey._ucItemType)   //低级别类型不能合高级别类型
            {
                continue;
            }
            else if (stDestGrid._stKey._ucItemType == stTakeoutGrid._stKey._ucItemType  //同一级别，低等级不能合高等级
                && stDestCardData._usLevel < stSourceCardData._usLevel)
            {
                continue;
            }
        }

        //获取基础经验
        unsigned int uiBaseExp = 0;
        CCardLevelConfigData stCardLevelConfigData;
        iRet = _stCardLevelConfig.GetConfig(1, stTakeoutGrid._stKey._ucItemType, uiBaseExp);
        CHECK_RETURN(iRet);

        //判断上限
        if (stDestCardData._iNowExp + stSourceCardData._iNowExp + uiBaseExp > uiMaxExp)
        {
            continue;
        }

        //取出卡片
        iRet = stCardStorage.TakeOutGrid(true, stGrid._stKey._usStorePos, stTakeoutGrid);
        CHECK_RETURN(iRet);

        //累加经验
        stDestCardData._iNowExp += stSourceCardData._iNowExp + uiBaseExp;

        //OSS账单 在清除之前调用
        CSingleCardData& stDelCardData = stUserCard._stCardHash[iIndex]._stCardData;
        CMainCtrl::m_pstMain->m_stOssMgr.AddConsumeItem(ITEM_TYPE_CARD, stDelCardData._usCardConfigID, 1, stDelCardData._uiCardID);

        //清除卡片
        stUserCard._stCardHash.HashFreeByIndex(iIndex);
    }

    //计算卡片等级
    iRet = GetCardLevel(stDestGrid._stKey._ucItemType, stDestCardData._usLevel, stDestCardData._iNowExp, stDestCardData._usLevel);
    CHECK_RETURN(iRet);

    //重新刷新背包位置
    iRet = GetData(stUserCard, stPlayerDetailInfo._stUserCardData);
    CHECK_RETURN(iRet);

    iRet = InitData(stUserCard, stUserCard._uiGID, stPlayerDetailInfo._stUserCardData);
    CHECK_RETURN(iRet);

    CT_TRACE(("CCardMgr: ============== after merge =============="));
    DumpCardInfo(stUserCard);

    //触发新手目标
    iIndex = CMainCtrl::m_pstMain->m_stPlayerDetailEx1InfoHash.HashFind(stUserCard._uiGID);
    if (iIndex >= 0)
    {
        CPlayerDetailInfoEx1& stPlayerDetailEx1Info = CMainCtrl::m_pstMain->m_stPlayerDetailEx1InfoHash[iIndex];

        CMainCtrl::m_pstMain->m_stNewbieTaskMgr.Trigger(stPlayerDetailEx1Info._stNewbieTaskUserData, stUserCard._uiGID, NEWBIE_TASK_COND_9, 
            stDestGrid._stKey._ucItemType * 10 + stDestCardData._usLevel);
    }

    return 0;
}

int CCardMgr::DropCard(CUserCard& stUserCard, unsigned short usCardConfigID, int iNowExp, bool bBroadCastMsg)
{
    int iRet = 0;
    if (stUserCard._stCardStorage.BagIsFull())
    {
        return ERROR_CARD_BAG_FULL;
    }

    CCardConfigData stConfigData;
    iRet = _stCardConfig.GetConfig(usCardConfigID, stConfigData);
    CHECK_RETURN(iRet);

    CSingleCardData stSingleCardData;
    stSingleCardData._uiCardID = ++stUserCard._iNowCardID;
    stSingleCardData._usCardConfigID = usCardConfigID;

    if (iNowExp <= 0)
    {
        stSingleCardData._usLevel = 1;
        stSingleCardData._iNowExp = 0;
    }
    else
    {
        CCardLevelConfigData stLevelConfigData;
        stSingleCardData._iNowExp = iNowExp;

        iRet = GetCardLevel(stConfigData._ucCardType, 1, iNowExp, stSingleCardData._usLevel);
        CHECK_RETURN(iRet);

        //触发新手目标
        int iIndex = CMainCtrl::m_pstMain->m_stPlayerDetailEx1InfoHash.HashFind(stUserCard._uiGID);
        if (iIndex >= 0)
        {
            CPlayerDetailInfoEx1& stPlayerDetailEx1Info = CMainCtrl::m_pstMain->m_stPlayerDetailEx1InfoHash[iIndex];

            CMainCtrl::m_pstMain->m_stNewbieTaskMgr.Trigger(stPlayerDetailEx1Info._stNewbieTaskUserData, stUserCard._uiGID, NEWBIE_TASK_COND_9, 
                                        stConfigData._ucCardType * 10 + stSingleCardData._usLevel);
        }
    }

    stSingleCardData._usPos = STORAGE_POS_NOT_OCCUPIED;
    stSingleCardData._ucCanWare = stConfigData._bDefaultCanWare;
    stSingleCardData._ucIsWare = 0;
    stSingleCardData._iWareRoleID = 0;

    //发送跑马灯
    if (bBroadCastMsg)
    {
        if (stConfigData._ucCardType == CARD_TYPE_GOLD_CARD)
        {
            MsgTips stTips;
            stTips.set_type(1001);
            stTips.add_tip_argument(CMainCtrl::m_pstMain->m_stRoleMgr.GetRoleName(stUserCard._uiGID));
            stTips.add_tip_argument(CTStringUtils::IntToString(usCardConfigID));
            CMainCtrl::m_pstMain->SendOnlineTips(stTips);
        }
        else if (stConfigData._ucCardType == CARD_TYPE_ORANGE_CARD)
        {
            MsgTips stTips;
            stTips.set_type(1002);
            stTips.add_tip_argument(CMainCtrl::m_pstMain->m_stRoleMgr.GetRoleName(stUserCard._uiGID));
            stTips.add_tip_argument(CTStringUtils::IntToString(usCardConfigID));
            CMainCtrl::m_pstMain->SendOnlineTips(stTips);
        }
    }
    
    iRet = AddCard(stUserCard, stSingleCardData);
    CHECK_RETURN(iRet);

    return 0;
}

//用于从占卜向背包掉落，usRoleLevel是用户当前级别
int CCardMgr::DropCard(CUserCard& stUserCard, unsigned char ucCardType, unsigned short usRoleLevel, int iNowExp)
{
    int iRet = 0;

    if (stUserCard._stCardStorage.BagIsFull())
    {
        return ERROR_CARD_BAG_FULL;
    }

    unsigned short usCardConfigID;
    iRet = _stCardConfig.Drop(usRoleLevel, ucCardType, usCardConfigID);
    CHECK_RETURN(iRet);

    return DropCard(stUserCard, usCardConfigID, iNowExp);
}

//抽取卡片经验， 只抽取蓝色及以下卡片
int CCardMgr::ExtractCardTotalExp(CUserDivineData& stUserDivineData, int& iTotalExp)
{
    int iRet = 0;
    iTotalExp = 0;

    CCardLevelConfigData stLevelConfigData;

    //蓝色卡片
    unsigned int uiBlueCardExp = 0;
    iRet = _stCardLevelConfig.GetConfig(1, CARD_TYPE_BLUE_CARD, uiBlueCardExp);
    CHECK_RETURN(iRet);
    iTotalExp += uiBlueCardExp * stUserDivineData._cBlueCard;
    stUserDivineData._cBlueCard = 0;

    //绿色卡片
    unsigned int uiGreenCardExp = 0;
    iRet = _stCardLevelConfig.GetConfig(1, CARD_TYPE_GREEN_CARD, uiGreenCardExp);
    CHECK_RETURN(iRet);
    iTotalExp += uiGreenCardExp * stUserDivineData._cGreenCard;
    stUserDivineData._cGreenCard = 0;

    return 0;
}

int CCardMgr::AddToBag(CUserCard& stUserCard, CSingleCardData& stSingleCardData)
{
    int iRet = 0;
    CCardConfigData stConfigData;
    iRet = _stCardConfig.GetConfig(stSingleCardData._usCardConfigID, stConfigData);
    CHECK_RETURN(iRet);

    CStorageGrid stGrid;
    stGrid._stKey._ucItemType = stConfigData._ucCardType;
    stGrid._stKey._usStorePos = stSingleCardData._usPos;
    stGrid._stKey._bIsInBag = true;
    stGrid._stKey._uiItemID = stSingleCardData._uiCardID;
    stGrid._shMaxPerGrid = 1;
    stGrid._shCount = 1;
    stGrid._iPosValue = GetPosValue(stConfigData._ucCardType, stSingleCardData._usLevel, stSingleCardData._usCardConfigID);
    iRet = stUserCard._stCardStorage.AddGrid(stGrid);
    CHECK_RETURN(iRet);

    stSingleCardData._usPos = stGrid._stKey._usStorePos;

    return 0;
}

int CCardMgr::GetPosValue(unsigned char ucCardType, unsigned short usCardLevel, unsigned short usCardConfigID)
{
    return ucCardType * 10000000 + usCardLevel * 10000 + usCardConfigID;
}

int CCardMgr::GetCardLevel(unsigned char ucCardType, unsigned short usFromLevel, unsigned int uiNowExp, unsigned short& usNewLevel)
{
    usNewLevel = usFromLevel;
    for (unsigned short i = usFromLevel; i < _stCardLevelConfig._stMaxLevelConfigData._usLevel; ++i)
    {
        unsigned int uiExp = 0;

        if (_stCardLevelConfig.GetConfig(i + 1, ucCardType, uiExp) != RET_OK)
        {
            break;
        }
        
        CT_TRACE(("%d, %d", uiNowExp, uiExp));
        if (uiNowExp < uiExp)
        {
            break;
        }
        else
        {
            CT_TRACE(("%d", i+1));
            usNewLevel = i + 1;
        }
    }

    return 0;
}

int CCardMgr::GetMergeValue(unsigned char ucCardType, unsigned short usCardLevel)
{
    if (ucCardType == CARD_TYPE_SOUL_CARD)
    {
        ucCardType = 0;     //卡魂必然被合成
    }

    return ucCardType * 1000 + usCardLevel; //先比较卡片颜色，再比较等级
}

int CCardMgr::WareCard(CUserCard& stUserCard, CPlayerWareCard& stPlayerWareCard, 
                       int iRoleID, unsigned short usRoleLevel, unsigned short usCardBagPos, unsigned char ucRolePos, CPlayerDetailInfo& stPlayerDetailInfo)
{
    int iRet = 0;

    //获取需要穿的卡片信息
    CStorageGrid stWareCardGrid;
    iRet = stUserCard._stCardStorage.GetGridInfoByPos(true, usCardBagPos, stWareCardGrid);
    CHECK_RETURN(iRet);

    if (stWareCardGrid.IsEmpty())
    {
        return ERROR_CARD_NOT_EXIST;
    }

    if (!IsValidWareCardPos(ucRolePos, usRoleLevel))
    {
        return ERROR_CARD_WARE_POS_NOT_OPEN;
    }

    //获取卡片配置
    int iIndex = stUserCard._stCardHash.HashFind(stWareCardGrid._stKey._uiItemID);
    if (iIndex < 0)
    {
        return ERROR_CARD_NOT_EXIST;
    }
    CSingleCardData& stWareCardData = stUserCard._stCardHash[iIndex]._stCardData;

    if (stWareCardData._ucCanWare == 0)
    {
        return ERROR_CARD_CAN_NOT_WARE;
    }

    if (stWareCardData._ucIsWare != 0)
    {
        return ERROR_CARD_HAS_WARE;
    }

    //相同属性的卡片只能戴一张
    CCardConfigData stCardConfigData;
    iRet = _stCardConfig.GetConfig(stWareCardData._usCardConfigID, stCardConfigData);
    CHECK_RETURN(iRet);

    CPlayerAttr stWareCardAttr;
    stCardConfigData.GetAttr(stWareCardAttr);

    CPlayerAttr stNowAttr;
    iRet = GetCardAttr(stUserCard, iRoleID, stPlayerDetailInfo, stNowAttr);
    CHECK_RETURN(iRet);

    if (stWareCardAttr.HasSameAttr(stNowAttr))
    {
        return ERROR_CARD_WARE_ATTR_SAME;
    }

    //获取身上的卡片id
    unsigned int uiNowCardID;
    iRet = stPlayerWareCard.GetCardByPos(ucRolePos, uiNowCardID);
    CHECK_RETURN(iRet);

    if (uiNowCardID != 0)
    {
        /*
        iIndex = stUserCard._stCardHash.HashFind(uiNowCardID);
        if (iIndex < 0)
        {
            return ERROR_CARD_NOT_EXIST;
        }
        CSingleCardData& stNowCardData = stUserCard._stCardHash[iIndex]._stCardData;

        if (stNowCardData._ucIsWare == 0)
        {
            return ERROR_CARD_NOT_WARE;
        }
        */
        return ERROR_CARD_WARE_POS;
    }
    
    //为了避免背包满，先取出卡片
    iRet = stUserCard._stCardStorage.TakeOutGrid(true, usCardBagPos, stWareCardGrid);
    CHECK_RETURN(iRet);

    if (uiNowCardID != 0) //有装备，需要先脱下来
    {
        iRet = TakeOffCard(stUserCard, stPlayerWareCard, ucRolePos, usCardBagPos);
        if (iRet != 0)
        {
            stUserCard._stCardStorage.AddToBag(stWareCardGrid);
        }
        CHECK_RETURN(iRet);
    }

    //穿卡片
    stWareCardData._usPos = ucRolePos;
    stWareCardData._ucIsWare = 1;
    stWareCardData._iWareRoleID = iRoleID;

    stPlayerWareCard.Ware(ucRolePos, stWareCardData._uiCardID);

    return 0;
}

int CCardMgr::MoveWareCard(CUserCard& stUserCard, CPlayerWareCard& stPlayerWareCard, int iRoleID, 
                                    unsigned short usRoleLevel, unsigned char ucFromPos, unsigned char ucToPos)
{
    int iRet = 0;

    if (!stPlayerWareCard.IsCanWare(usRoleLevel, ucToPos)
        || !stPlayerWareCard.IsWare(ucFromPos)
        || stPlayerWareCard.IsWare(ucToPos))
    {
        return ERROR_CARD_CAN_NOT_WARE;
    }

    if (!IsValidWareCardPos(ucToPos, usRoleLevel))
    {
        return ERROR_CARD_WARE_POS_NOT_OPEN;
    }

    unsigned int uiCardID = 0;
    iRet = stPlayerWareCard.GetCardByPos(ucFromPos, uiCardID);
    CHECK_RETURN(iRet);

    int iIndex = stUserCard._stCardHash.HashFind(uiCardID);
    if (iIndex < 0)
    {
        return ERROR_CARD_NOT_EXIST;
    }
    CSingleCardData& stWareCardData = stUserCard._stCardHash[iIndex]._stCardData;

    if (stWareCardData._ucIsWare == 0
        || stWareCardData._iWareRoleID != iRoleID
        || stWareCardData._usPos != ucFromPos)
    {
        CT_ERROR(("%d %d %d %d %d", stWareCardData._ucIsWare, stWareCardData._iWareRoleID, iRoleID, stWareCardData._usPos, ucFromPos));
        return RET_SYS_ERROR;
    }
 
    iRet = stPlayerWareCard.TakeOff(ucFromPos);
    CHECK_RETURN(iRet);

    iRet = stPlayerWareCard.Ware(ucToPos, uiCardID);
    CHECK_RETURN(iRet);

    stWareCardData._usPos = ucToPos;

    return 0;
}

int CCardMgr::TakeOffCard(CUserCard& stUserCard, CPlayerWareCard& stPlayerWareCard, 
                      unsigned char ucRolePos, unsigned short& usCardBagPos)
{
    int iRet = 0;

    if (stUserCard._stCardStorage.BagIsFull())
    {
        return ERROR_CARD_BAG_FULL;
    }

    //数据检查
    unsigned int uiNowCardID;
    iRet = stPlayerWareCard.GetCardByPos(ucRolePos, uiNowCardID);
    CHECK_RETURN(iRet);

    if (uiNowCardID == 0)
    {
        return ERROR_CARD_NO_CARD_IN_POS;
    }

    //检查背包位置
    if (usCardBagPos != STORAGE_POS_NOT_OCCUPIED)
    {
        CStorageGrid stCardGrid;
        iRet = stUserCard._stCardStorage.GetGridInfoByPos(true, usCardBagPos, stCardGrid);
        CHECK_RETURN(iRet);

        if (!stCardGrid.IsEmpty())
        {
            return ERROR_CARD_HAS_CARD_IN_POS;
        }
    }

    int iIndex = stUserCard._stCardHash.HashFind(uiNowCardID);
    if (iIndex < 0)
    {
        return ERROR_CARD_NOT_EXIST;
    }
    CSingleCardData& stCardData = stUserCard._stCardHash[iIndex]._stCardData;

    if (stCardData._ucIsWare == 0)
    {
        return ERROR_CARD_NOT_WARE;
    }
    
    //脱卡片
    iRet = stPlayerWareCard.TakeOff(ucRolePos);
    CHECK_RETURN(iRet);
       
    //放入背包
    stCardData._usPos = usCardBagPos;
    stCardData._ucIsWare = 0;
    stCardData._iWareRoleID = 0;
    iRet = AddToBag(stUserCard, stCardData);
    CHECK_RETURN(iRet);
    usCardBagPos = stCardData._usPos;

    return 0;
}

int CCardMgr::TakeOffCard(CUserCard& stUserCard, CPlayerDetailInfo& stPlayerDetailInfo, 
                int iRoleID, unsigned char ucRolePos, unsigned int uiCardID)
{
    int iRet = 0;
    //脱下卡片
    if (iRoleID == 0) //主角
    {
        unsigned int uiTakeoffCardID = 0;
        stPlayerDetailInfo._stPlayerWareCard.GetCardByPos(ucRolePos, uiTakeoffCardID);
        if (uiTakeoffCardID != uiCardID)
        {
            CT_ERROR(("dest %u, ware %u", uiCardID, uiTakeoffCardID));
            return ERROR_CARD_ID;
        }

        iRet = stPlayerDetailInfo._stPlayerWareCard.TakeOff(ucRolePos);
        CHECK_RETURN(iRet);
    }
    else
    {
        int iIndex = stPlayerDetailInfo._stUserPartner.FindActData((short)iRoleID);
        if (iIndex < 0)
        {
            CT_ERROR((""));
            return ERROR_CARD_NO_PARTNER;
        }

        CPartnerActData& stPartner = stPlayerDetailInfo._stUserPartner.GetPartnerActData(iIndex);

        unsigned int uiTakeoffCardID = 0;
        stPartner._stPlayerWareCard.GetCardByPos(ucRolePos, uiTakeoffCardID);
        if (uiTakeoffCardID != uiCardID)
        {
            CT_ERROR(("dest %u, ware %u", uiCardID, uiTakeoffCardID));
            return ERROR_CARD_ID;
        }

        iRet = stPartner._stPlayerWareCard.TakeOff(ucRolePos);
        CHECK_RETURN(iRet);
    }

    return 0;
}

int CCardMgr::WareCard(CUserCard& stUserCard, CPlayerDetailInfo& stPlayerDetailInfo, 
             int iRoleID, unsigned char ucRolePos, unsigned int uiCardID)
{
    int iRet = 0;

    //穿上卡片
    if (iRoleID == 0) //主角
    {
        iRet = stPlayerDetailInfo._stPlayerWareCard.Ware(ucRolePos, uiCardID);
        CHECK_RETURN(iRet);
    }
    else
    {
        int iIndex = stPlayerDetailInfo._stUserPartner.FindActData((short)iRoleID);
        if (iIndex < 0)
        {
            CT_ERROR((""));
            return ERROR_CARD_NO_PARTNER;
        }

        CPartnerActData& stPartner = stPlayerDetailInfo._stUserPartner.GetPartnerActData(iIndex);

        iRet = stPartner._stPlayerWareCard.Ware(ucRolePos, uiCardID);
        CHECK_RETURN(iRet);
    }

    return 0;
}

int CCardMgr::GetVortexConfig(unsigned short usLevel, CVortexConfigData& stConfigData)
{
    if (usLevel > _stVortexConfig.usMaxLevel)
    {
        usLevel = _stVortexConfig.usMaxLevel;
    }

    return _stVortexConfig.GetConfig(usLevel, stConfigData);
}

int CCardMgr::VortexLevelUp(int& iNowFateForce, CCardVortex& stCardVortex)
{
    int iRet = 0;

    if (stCardVortex._usLevel >= _stVortexConfig.usMaxLevel) //已经满级
    {
        return 0;
    }
    
    //获取配置
    CVortexConfigData stNextLevelConfigData;
    iRet = GetVortexConfig(stCardVortex._usLevel + 1, stNextLevelConfigData);
    CHECK_RETURN(iRet);

    //检查数据
    int iNeedFateForce = (int)stNextLevelConfigData._uiNeedFateForceCount - stCardVortex._iTotalFateForce;
    if (iNeedFateForce <= iNowFateForce)
    {
        iNowFateForce -= iNeedFateForce;
        stCardVortex._iTotalFateForce += iNeedFateForce;
    }
    else
    {
        return ERROR_CARD_NOT_ENOUGH_FATE_FORCE;
    }

    iRet = GetVortexLevel(stCardVortex._iTotalFateForce, stCardVortex._usLevel);
    CHECK_RETURN(iRet);

    return 0;
}

int CCardMgr::VortexLevelDown(int& iNowFateForce, CPlayerBaseInfo& stPlayerBaseInfo, CCardVortex& stCardVortex, bool bDownAll)
{
    int iRet = 0;

    if (stCardVortex._usLevel == 0) //0级，无法继续下降
    {
        return 0;
    }

    CVortexConfigData stThisLevelConfigData;
    iRet = GetVortexConfig(stCardVortex._usLevel, stThisLevelConfigData);
    CHECK_RETURN(iRet);

    //检查并扣除银币
    unsigned int uiNeedSilver = bDownAll ? stThisLevelConfigData._uiExtractToZeroNeedSilver 
                                           : stThisLevelConfigData._uiExtractNeedSilver;    

    iRet = CMainCtrl::m_pstMain->m_stItemMgr.SubSilver(stPlayerBaseInfo, (int)uiNeedSilver, 0);
    CHECK_RETURN(iRet);

    if (!bDownAll)
    {
        int iBackFactForce = 0; //需返还的命运之力
        CVortexConfigData stLastLevelConfigData;        //上一级
        iRet = GetVortexConfig(stCardVortex._usLevel - 1, stLastLevelConfigData);
        CHECK_RETURN(iRet);
        iBackFactForce = stThisLevelConfigData._uiNeedFateForceCount - stLastLevelConfigData._uiNeedFateForceCount;

        if (stCardVortex._iTotalFateForce < iBackFactForce)
        {
            return RET_SYS_ERROR;
        }
        else
        {
            stCardVortex._iTotalFateForce -= iBackFactForce;
            iNowFateForce += iBackFactForce;
            iRet = GetVortexLevel(stCardVortex._iTotalFateForce, stCardVortex._usLevel);
            CHECK_RETURN(iRet);
        }
    }
    else
    {
        iNowFateForce += stCardVortex._iTotalFateForce;
        stCardVortex._iTotalFateForce = 0;
        stCardVortex._usLevel = 0;
    }

    return 0;
}

int CCardMgr::GetVortexLevel(int iFateForce, unsigned short& usLevel)
{
    int iRet = 0;
    for (unsigned short i = 0 ;i <= MAX_VORTEX_CONFIG_COUNT; ++i)
    {
        CVortexConfigData stConfigData;
        iRet = GetVortexConfig(i, stConfigData);
        CHECK_RETURN(iRet);

        if (iFateForce >= (int)stConfigData._uiNeedFateForceCount)
        {
            usLevel = i;
        }
    }

    return 0;
}

int CCardMgr::CardCombine(CUserCard& stUserCard, CPlayerDetailInfo& stPlayerDetailInfo, unsigned int uiFromCardID, unsigned int uiToCardID, bool bDestIsTo, unsigned int& uiDestCardID)
{
    int iRet = 0;
    int iIndex ;
    iIndex = stUserCard._stCardHash.HashFind(uiFromCardID);
    if (iIndex < 0)
    {
        return ERROR_CARD_ID;
    }
    CSingleCardData& stFromCardData = stUserCard._stCardHash[iIndex]._stCardData;

    iIndex = stUserCard._stCardHash.HashFind(uiToCardID);
    if (iIndex < 0)
    {
        return ERROR_CARD_ID;
    }
    CSingleCardData& stToCardData = stUserCard._stCardHash[iIndex]._stCardData;

    CCardConfigData stFromCardConfigData;
    iRet = _stCardConfig.GetConfig(stFromCardData._usCardConfigID, stFromCardConfigData);
    CHECK_RETURN(iRet);

    CCardConfigData stToCardConfigData;
    iRet = _stCardConfig.GetConfig(stToCardData._usCardConfigID, stToCardConfigData);
    CHECK_RETURN(iRet);

    //检查：优先级
    int iFromCardMergeValue = GetMergeValue(stFromCardConfigData._ucCardType, stFromCardData._usLevel);
    int iToCardMergeValue = GetMergeValue(stToCardConfigData._ucCardType, stToCardData._usLevel);

    if (iToCardMergeValue > iFromCardMergeValue)
    {
        //交换合并方向
        return CardCombine(stUserCard, stPlayerDetailInfo, uiToCardID, uiFromCardID, !bDestIsTo, uiDestCardID);
    }
    else
    {
        //相同属性的卡片只能戴一张, 目标卡片必须将要被穿上才检查
        if (stFromCardData._ucIsWare == 0 && stToCardData._ucIsWare == 1)
        {
            CCardConfigData stFromCardConfigData;
            iRet = _stCardConfig.GetConfig(stFromCardData._usCardConfigID, stFromCardConfigData);
            CHECK_RETURN(iRet);

            CCardConfigData stToCardConfigData;
            iRet = _stCardConfig.GetConfig(stToCardData._usCardConfigID, stToCardConfigData);
            CHECK_RETURN(iRet);

            CPlayerAttr stWareCardAttr;
            stFromCardConfigData.GetAttr(stWareCardAttr);

            CPlayerAttr stUnWareCardAttr;
            stToCardConfigData.GetAttr(stUnWareCardAttr);

            CPlayerAttr stNowAttr;
            iRet = GetCardAttr(stUserCard, stToCardData._iWareRoleID, stPlayerDetailInfo, stNowAttr);
            CHECK_RETURN(iRet);

            if (!stWareCardAttr.HasSameAttr(stUnWareCardAttr) && stWareCardAttr.HasSameAttr(stNowAttr))
            {
                return ERROR_CARD_WARE_ATTR_SAME;
            }
        }
    }

    //检查：等级
    if (stFromCardData._usLevel >= _stCardLevelConfig._stMaxLevelConfigData._usLevel)
    {
        return ERROR_CARD_LEVEL_IS_MAX;
    }

    //检查：经验是否会溢出
    unsigned int uiToMaxLevelExp = 0; //升到满级所需要的经验
    _stCardLevelConfig._stMaxLevelConfigData.GetExp(stFromCardConfigData._ucCardType, uiToMaxLevelExp); 
    uiToMaxLevelExp -= stFromCardData._iNowExp;
    
    unsigned int uiCanGetExp  = 0; //可以获得的经验
    _stCardLevelConfig.GetConfig(1, stToCardConfigData._ucCardType, uiCanGetExp);   //基础经验
    uiCanGetExp += stToCardData._iNowExp;

    if (uiCanGetExp > uiToMaxLevelExp)
    {
        return ERROR_CARD_MERGE_TOO_MANY_EXP;
    }

    //合并
    //将目标卡片取出
    bool bIsWare = (stToCardData._ucIsWare != 0);
    int iWareRoleID = stToCardData._iWareRoleID;
    unsigned short usPos = stToCardData._usPos;
    
    if (bIsWare)
    {
        iRet = TakeOffCard(stUserCard, stPlayerDetailInfo, iWareRoleID, (unsigned char)usPos, stToCardData._uiCardID);
        CHECK_RETURN(iRet);
    }
    else
    {
        //从背包取出卡片
        CStorageGrid stGrid;
        iRet = stUserCard._stCardStorage.TakeOutGrid(true, usPos, stGrid);
        CHECK_RETURN(iRet);

        if (stGrid._stKey._uiItemID != stToCardData._uiCardID)
        {
            CT_ERROR(("dest %u, store %u", stToCardData._uiCardID, stGrid._stKey._uiItemID));
            return ERROR_CARD_ID; 
        }
    }

    //OSS账单 在释放之前调用
    CMainCtrl::m_pstMain->m_stOssMgr.AddConsumeItem(ITEM_TYPE_CARD, stToCardData._usCardConfigID, 1, stToCardData._uiCardID);

    //释放卡片
    stUserCard._stCardHash.HashFreeByKey(stToCardData._uiCardID);

    //添加经验
    stFromCardData._iNowExp += uiCanGetExp;

    //计算等级
    unsigned short usOrgLevel = stFromCardData._usLevel;
    iRet = GetCardLevel(stFromCardConfigData._ucCardType, usOrgLevel, stFromCardData._iNowExp, stFromCardData._usLevel);
    CHECK_RETURN(iRet);
    bool bIsUpdate = (stFromCardData._usLevel > usOrgLevel);

    //触发新手目标
    iIndex = CMainCtrl::m_pstMain->m_stPlayerDetailEx1InfoHash.HashFind(stUserCard._uiGID);
    if (iIndex >= 0)
    {
        CPlayerDetailInfoEx1& stPlayerDetailEx1Info = CMainCtrl::m_pstMain->m_stPlayerDetailEx1InfoHash[iIndex];

        CMainCtrl::m_pstMain->m_stNewbieTaskMgr.Trigger(stPlayerDetailEx1Info._stNewbieTaskUserData, stUserCard._uiGID, NEWBIE_TASK_COND_9, 
            stFromCardConfigData._ucCardType * 10 + stFromCardData._usLevel);
    }

    //检查位置：放入背包或者穿上
    if (bDestIsTo) 
    {
        if (stFromCardData._ucIsWare)   //先脱下
        {
            iRet = TakeOffCard(stUserCard, stPlayerDetailInfo, stFromCardData._iWareRoleID, (unsigned char)stFromCardData._usPos, stFromCardData._uiCardID);
            CHECK_RETURN(iRet);
        }
        else
        {
            //从背包取出卡片
            CStorageGrid stGrid;
            iRet = stUserCard._stCardStorage.TakeOutGrid(true, stFromCardData._usPos, stGrid);
            CHECK_RETURN(iRet);

            if (stGrid._stKey._uiItemID != stFromCardData._uiCardID)
            {
                CT_ERROR(("dest %u, store %u", stFromCardData._uiCardID, stGrid._stKey._uiItemID));
                return ERROR_CARD_ID; 
            }
        }

        if (bIsWare)    
        {
            iRet = WareCard(stUserCard, stPlayerDetailInfo, iWareRoleID, (unsigned char)usPos, stFromCardData._uiCardID);
            stFromCardData._ucIsWare = 1;
            stFromCardData._iWareRoleID = iWareRoleID;
            stFromCardData._usPos = usPos;

            CMainCtrl::m_pstMain->m_stNotifyMgr.Notify(stPlayerDetailInfo._uiGID, NOTIFY_CARD_POINT, iWareRoleID);
        }
        else
        {
            //放入仓库
            stFromCardData._ucIsWare = 0;
            stFromCardData._iWareRoleID = 0;
            stFromCardData._usPos = usPos;
            iRet = AddToBag(stUserCard, stFromCardData);
            CHECK_RETURN(iRet);
        }
    }

    if (stFromCardData._ucIsWare && bIsUpdate) //重新计算属性
    {
        int iIndex = CMainCtrl::m_pstMain->m_stPlayerBaseInfoHash.HashFind(stPlayerDetailInfo._uiGID);
        if (iIndex >= 0)
        {
            CPlayerBaseInfo& stPlayerBaseInfo = CMainCtrl::m_pstMain->m_stPlayerBaseInfoHash[iIndex];

            iIndex = CMainCtrl::m_pstMain->m_stPlayerOnlineDataHash.HashFind(stPlayerDetailInfo._uiGID);
            if (iIndex >= 0)
            {
                if (0 == stFromCardData._iWareRoleID)
                {
                    CMainCtrl::m_pstMain->m_stNotifyMgr.Notify(stPlayerBaseInfo._uiGID, NOTIFY_ROLE_ATTR);
                }
                else
                {   
                    CMainCtrl::m_pstMain->m_stNotifyMgr.Notify(stPlayerBaseInfo._uiGID, NOTIFY_PARTNER_ATTR, stFromCardData._iWareRoleID);
                }
            }
        }
    }

    uiDestCardID = stFromCardData._uiCardID;

    return 0;
}

int CCardMgr::SwapStorageItemPos(CUserCard& stUserCard, int iItemPos1,CStorageGrid & stGrid1, int iItemPos2,CStorageGrid & stGrid2)
{
    int iRet = 0;

    //检查源位置必须有卡片
    iRet = stUserCard._stCardStorage.GetGridInfoByPos(true, iItemPos1, stGrid1);
    CHECK_RETURN(iRet);

    if (stGrid1.IsEmpty())
    {
        return ERROR_CARD_NO_CARD_IN_POS;
    }

    iRet = stUserCard._stCardStorage.GetGridInfoByPos(true, iItemPos2, stGrid2);
    CHECK_RETURN(iRet);

    //检查目标位不能有卡片
    if (!stGrid2.IsEmpty())
    {
        CT_ERROR((""));
        return RET_SYS_ERROR;
    }

    int iIndex = stUserCard._stCardHash.HashFind(stGrid1._stKey._uiItemID);
    if (iIndex < 0)
    {
        return ERROR_CARD_ID;
    }
    CSingleCardData& stCardData = stUserCard._stCardHash[iIndex]._stCardData;

    iRet = stUserCard._stCardStorage.SwapStorageItemPos(STORAGE_TYPE_BAG, iItemPos1, stGrid1,
                                            STORAGE_TYPE_BAG, iItemPos2, stGrid2);
    CHECK_RETURN(iRet);

    stCardData._usPos = iItemPos2;
    
    return RET_OK;
}

int CCardMgr::ExchangeCard(CUserCard& stUserCard, unsigned short usCardConfigID, unsigned short usRoleLevel, int& iRomany, CPlayerDetailInfo& stPlayerDetailInfo)
{
    int iRet = 0;

    //检查
    if (stUserCard._stCardStorage.BagIsFull())
    {
        return ERROR_CARD_BAG_FULL;
    }

    CCardConfigData stCardConfig;
    iRet = _stCardConfig.GetConfig(usCardConfigID, stCardConfig);
    CHECK_RETURN(iRet);

    if (!stCardConfig._bCanExchange)
    {
        return ERROR_CARD_CAN_NOT_EXCHANGE;
    }

    if (usRoleLevel < stCardConfig._usExchangeLevel)
    {
        return ERROR_CARD_EXCHANGE_LOW_LEVEL;
    }

    if (stCardConfig._iExchangePrice <= 0)
    {
        return RET_SYS_ERROR;
    }

    if (iRomany < stCardConfig._iExchangePrice)
    {
        return ERROR_ROMANY_LESS;
    }
    iRomany -= stCardConfig._iExchangePrice;

    if (CARD_TYPE_GOLD_CARD == stCardConfig._ucCardType)
    {
        //精彩活动：金秋猎金
        CMainCtrl::m_pstMain->m_stActMgr.TriggerGetCard(stPlayerDetailInfo._stSplendidAct, 
            stPlayerDetailInfo._uiGID, stCardConfig._ucCardType, stCardConfig._usCardConfigID, 1);
    }

    //发放卡片
    iRet = DropCard(stUserCard, usCardConfigID, 0, false);
    CHECK_RETURN(iRet);

    return 0;
}

void CCardMgr::SendDivineInfoToFront(CUserCard& stUserCard, CPlayerBaseInfo& stPlayerBaseInfo, CUserDivineData& stUserDivineData, PBMsgHead stMsgHead, CTLib::CEnpNetHead& stEnpHead)
{
    stMsgHead.set_msgret(RET_OK);
    stMsgHead.set_msgid(ID_MAIN_CARD_DIVINE_INFO);
    stMsgHead.set_msgtype(CSMsgDef::EMT_ANSWER);
    
    MsgAnsDivineInfo stAns;
    stAns.set_last_card(stUserDivineData.ucCardPos);

    MsgUserGold* pstMsgUserGold = stAns.mutable_gold();
    pstMsgUserGold->set_vip_level(stPlayerBaseInfo._chVipLevel);
    pstMsgUserGold->set_gold(stPlayerBaseInfo._iGold);
    pstMsgUserGold->set_romany(stPlayerBaseInfo._iRomany);
    CTSizeString<50> szSilver;
    szSilver.Set("%ld", stPlayerBaseInfo._i64Silver);
    pstMsgUserGold->set_silver(szSilver());

    MsgDivineInfo* pstMsgDivineInfo = stAns.mutable_divine_info();
    pstMsgDivineInfo->set_silver(stUserDivineData._iSilver);
    pstMsgDivineInfo->set_fate_force(stUserDivineData._shFateForce);
    pstMsgDivineInfo->set_green_card(stUserDivineData._cGreenCard);
    pstMsgDivineInfo->set_blue_card(stUserDivineData._cBlueCard);
    pstMsgDivineInfo->set_purple_card(stUserDivineData._cPurpleCard);
    pstMsgDivineInfo->set_gold_card(stUserDivineData._cGoldCard);
    pstMsgDivineInfo->set_soul_card(stUserDivineData._cSoulCard);
    pstMsgDivineInfo->set_romany(stUserDivineData._cRomany);

    CMainCtrl::m_pstMain->m_stEncodeBuf.Clear();
    CMainCtrl::m_pstMain->EncodePBMsg(stMsgHead, CMainCtrl::m_pstMain->m_stEncodeBuf);
    CMainCtrl::m_pstMain->EncodePBMsg(stAns, CMainCtrl::m_pstMain->m_stEncodeBuf);

    CMainCtrl::m_pstMain->AnswerToClient(stEnpHead, CMainCtrl::m_pstMain->m_stEncodeBuf);
}

void CCardMgr::SendCardBagInfoToFront(CUserCard& stUserCard, PBMsgHead stMsgHead, CTLib::CEnpNetHead& stEnpHead)
{
    MsgAnsUserStorageInfo stAns;
    CStorageMgr::T_STORAGE_GRIDS astStorageGrid;

    stMsgHead.set_msgid(ID_MAIN_GET_USER_STORAGE_INFO);
    stMsgHead.set_msgtype(CSMsgDef::EMT_ANSWER);
    stAns.set_gid(stUserCard._uiGID);
    stAns.set_storage_type(STORAGE_TYPE_CARD);

    stAns.set_grid_total_num(MAX_CARD_BUG_GRID_COUNT);
    stAns.set_grid_default_num(MAX_CARD_BUG_GRID_COUNT);
    stAns.set_grid_unlocked_num(0);
    CMainCtrl::m_pstMain->m_stStorageMgr.GetBagList(stUserCard._stCardStorage, astStorageGrid);

    for (int i = 0; i < astStorageGrid.Size(); ++i)
    {
        CStorageGrid& stStorageGrid = astStorageGrid[i];

        MsgGridItemInfo* pstGridItemInfo = stAns.add_grid_items();

        int iIndex = stUserCard._stCardHash.HashFind(stStorageGrid._stKey._uiItemID);
        if (iIndex < 0)
        {
            CT_WARNING(("invalid card id %u", stStorageGrid._stKey._uiItemID));
            continue;
        }
        CSingleCardData& stSingleCardData = stUserCard._stCardHash[iIndex]._stCardData;
        
        pstGridItemInfo->set_item_store_pos(stStorageGrid._stKey._usStorePos);
        pstGridItemInfo->set_item_type(ITEM_TYPE_CARD);
        pstGridItemInfo->set_item_count(1);
        pstGridItemInfo->set_item_config_id(stSingleCardData._usCardConfigID);
        pstGridItemInfo->set_item_aux_info(CTStringUtils::Uint64ToString(stStorageGrid._stKey._uiItemID, 10));
    }

    CMainCtrl::m_pstMain->m_stEncodeBuf.Clear();
    CMainCtrl::m_pstMain->EncodePBMsg(stMsgHead, CMainCtrl::m_pstMain->m_stEncodeBuf);
    CMainCtrl::m_pstMain->EncodePBMsg(stAns, CMainCtrl::m_pstMain->m_stEncodeBuf);

    CMainCtrl::m_pstMain->AnswerToClient(stEnpHead, CMainCtrl::m_pstMain->m_stEncodeBuf);
}

void CCardMgr::GetCardDetailInfo(CUserCard& stUserCard, CPlayerDetailInfo& stPlayerDetailInfo, 
                                 const ::google::protobuf::RepeatedField<int64>& stReqIDList, MsgAnsItemDetailInfos& stAns)
{
    stAns.set_item_type(ITEM_TYPE_CARD);
    stAns.set_gid(stUserCard._uiGID);

    for (int i = 0; i < stReqIDList.size(); ++i)
    {
        unsigned int uiCardID = CT_UINT_LOW(stReqIDList.Get(i));

        MsgCardDetailInfo* pstCardInfo = stAns.add_card_infos();
        GetCardInfo(stUserCard, stPlayerDetailInfo, uiCardID, *pstCardInfo);
    }
}

void CCardMgr::SendCardDetailInfoToFront(CUserCard& stUserCard, CPlayerDetailInfo& stPlayerDetailInfo, const ::google::protobuf::RepeatedField<int64>& stReqIDList, 
                               PBMsgHead stMsgHead, CEnpNetHead stEnpHead)
{
    MsgAnsItemDetailInfos stAns;
    GetCardDetailInfo(stUserCard, stPlayerDetailInfo, stReqIDList, stAns);

    CMainCtrl::m_pstMain->m_stEncodeBuf.Clear();
    CMainCtrl::m_pstMain->EncodePBMsg(stMsgHead, CMainCtrl::m_pstMain->m_stEncodeBuf);
    CMainCtrl::m_pstMain->EncodePBMsg(stAns, CMainCtrl::m_pstMain->m_stEncodeBuf);

    CMainCtrl::m_pstMain->AnswerToClient(stEnpHead, CMainCtrl::m_pstMain->m_stEncodeBuf);

}

void CCardMgr::SendCardOnRoleToFront(CUserCard& stUserCard, CPlayerBaseInfo& stPlayerBaseInfo, CPlayerDetailInfo& stPlayerDetailInfo, PBMsgHead stMsgHead, CEnpNetHead stEnpHead)
{
    MsgAnsUserStorageInfo stAns;
    CStorageMgr::T_STORAGE_GRIDS astStorageGrid;

    stMsgHead.set_msgid(ID_MAIN_GET_USER_STORAGE_INFO);
    stMsgHead.set_msgtype(CSMsgDef::EMT_ANSWER);
    stAns.set_gid(stUserCard._uiGID);
    stAns.set_storage_type(STORAGE_TYPE_ROLE_CARD);

    int iCanWareCardCount = stPlayerDetailInfo._stPlayerWareCard.CanWareCardCount(stPlayerBaseInfo._shLevel);
    stAns.set_grid_total_num(MAX_CARD_COUNT_PER_ROLE);
    stAns.set_grid_default_num(1);
    stAns.set_grid_unlocked_num(iCanWareCardCount);

    for (int i = 0; i < iCanWareCardCount; ++i)
    {
        unsigned int uiCardID = 0;
        if (RET_OK != stPlayerDetailInfo._stPlayerWareCard.GetCardByPos((unsigned char)i, uiCardID))
        {
            continue;
        }

        MsgGridItemInfo* pstGridItemInfo = stAns.add_grid_items();
        pstGridItemInfo->set_item_store_pos(i);

        if (uiCardID == 0)  //没有卡片
        {
            pstGridItemInfo->set_item_count(0);
            pstGridItemInfo->set_item_config_id(0);            
            pstGridItemInfo->set_item_type(0);
        }
        else
        {
            int iIndex = stUserCard._stCardHash.HashFind(uiCardID);
            if (iIndex < 0)
            {
                CT_WARNING(("invalid card id %u", uiCardID));
                continue;
            }
            CSingleCardData& stSingleCardData = stUserCard._stCardHash[iIndex]._stCardData;

            pstGridItemInfo->set_item_config_id(stSingleCardData._usCardConfigID);
            pstGridItemInfo->set_item_count(1);
            pstGridItemInfo->set_item_aux_info(CTStringUtils::Uint64ToString(uiCardID, 10));
            pstGridItemInfo->set_item_type(ITEM_TYPE_CARD);
        }
    }

    CMainCtrl::m_pstMain->m_stEncodeBuf.Clear();
    CMainCtrl::m_pstMain->EncodePBMsg(stMsgHead, CMainCtrl::m_pstMain->m_stEncodeBuf);
    CMainCtrl::m_pstMain->EncodePBMsg(stAns, CMainCtrl::m_pstMain->m_stEncodeBuf);

    CMainCtrl::m_pstMain->AnswerToClient(stEnpHead, CMainCtrl::m_pstMain->m_stEncodeBuf);
}

void CCardMgr::DumpCardInfo(CUserCard& stUserCard)
{
    //HASH信息
    for (int i = stUserCard._stCardHash.GetHeadIndex();
        i >= 0; i = stUserCard._stCardHash.GetNextIndex(i))
    {
        CSingleCardData& stCardData = stUserCard._stCardHash[i]._stCardData;
        CT_TRACE(("CCardMgr: card in hash: id %u, pos %d",
            stCardData._uiCardID, stCardData._usPos));
    }

    //背包信息
    CStorageMgr::T_STORAGE_GRIDS astStorageGrid;
    CMainCtrl::m_pstMain->m_stStorageMgr.GetBagList(stUserCard._stCardStorage, astStorageGrid);
    for (int i = 0; i < astStorageGrid.Size(); ++i)
    {
        CStorageGrid& stGird = astStorageGrid[i];
        CT_TRACE(("CCardMgr: card in bag: id %u, pos %d",
            stGird._stKey._uiItemID, stGird._stKey._usStorePos));
    }
}

int CCardMgr::GetCardPoint(CUserCard& stUserCard, unsigned int uiCardID, unsigned int& uiPoint)
{
    int iRet = 0;
    uiPoint = 0;

    int iIndex = stUserCard._stCardHash.HashFind(uiCardID);
    if (iIndex < 0)
    {
        return ERROR_CARD_ID;
    }
    CSingleCardData& stCardData = stUserCard._stCardHash[iIndex]._stCardData;

    CCardConfigData stCardConfigData;
    iRet = _stCardConfig.GetConfig(stCardData._usCardConfigID, stCardConfigData);
    CHECK_RETURN(iRet);

    iRet = _stCardLevelConfig.GetPointConfig(stCardData._usLevel, stCardConfigData._ucCardType, uiPoint);
    CHECK_RETURN(iRet);

    uiPoint += stCardData._iNowExp / 30;

    return 0;
}   

int CCardMgr::GetCardPoint(CUserCard& stUserCard, CPlayerWareCard& stPlayerWareCard, unsigned int& uiPoint)
{
    uiPoint = 0;
    for (int i = 0; i < stPlayerWareCard._astPlayerCard.Size(); ++i)
    {
        unsigned int uiCardID = stPlayerWareCard._astPlayerCard[i];
        if (uiCardID == 0)
        {
            continue;
        }

        unsigned int uiTmp = 0;
        GetCardPoint(stUserCard, stPlayerWareCard._astPlayerCard[i], uiTmp);
        uiPoint += uiTmp;
    }

    return 0;
}

void CCardMgr::GetWareCardDetialInfo(CUserCard& stUserCard, CPlayerDetailInfo& stPlayerDetailInfo, 
                               CPlayerWareCard& stPlayerWareCard, ::google::protobuf::RepeatedPtrField<MsgCardDetailInfo>& stDetailInfo)
{
    for (int i = 0; i < stPlayerWareCard._astPlayerCard.Size(); ++i)
    {
        unsigned int uiCardID = stPlayerWareCard._astPlayerCard[i];
        if (uiCardID == 0)
        {
            continue;
        }

        GetCardInfo(stUserCard, stPlayerDetailInfo, uiCardID, *(stDetailInfo.Add()));
    }
}

int CCardMgr::GetGoldTurnOverCount(unsigned char ucVipLevel, int iHasOptCount)
{
    int iRet = 0;
    int iMaxDivineCount = 0;
    iRet = CMainCtrl::m_pstMain->m_stVipMgr.GetVipDestNum(ucVipLevel, VIP_MODULE_ID_25, iMaxDivineCount);
    if (RET_OK != iRet)
    {
        CT_WARNING((""));
        return 0;
    }

    return iMaxDivineCount - iHasOptCount;
}

